package com.wxapp.dispatch.service.util;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wxapp.common.base.util.jackson.JacksonUtil;
import com.wxapp.common.util.check.CheckUtil;
import com.wxapp.common.util.copy.CopyUtil;
import com.wxapp.dispatch.api.pojo.entity.DispatchDO;
import com.wxapp.dispatch.api.pojo.vm.dispatch.PDispatchDispatchVm;
import com.wxapp.dispatch.api.pojo.vo.dispatch.QDispatchVo;
import com.wxapp.dispatch.service.mapper.DispatchMapper;
import lombok.RequiredArgsConstructor;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 定时任务增删改查
 */
@Component
@RequiredArgsConstructor
public class JobUtil implements CommandLineRunner {
    private final Logger log = LoggerFactory.getLogger(JobUtil.class);
    private final DispatchMapper dispatchMapper;
    @Override
    public void run(String... args) throws Exception {
        var query = new LambdaQueryWrapper<DispatchDO>();
        query.eq(DispatchDO::getStatus, true).eq(DispatchDO::getOpen,true)
                .orderByAsc(DispatchDO::getWeight);
        var dispatchDOS = dispatchMapper.selectList(query);
        if (CheckUtil.isNotNullList(dispatchDOS)) {
            addOrUpdateJob(CopyUtil.copyListNew(dispatchDOS, QDispatchVo::new, null));
        }
    }

    /**
     * 添加或者更新job
     */
    public void addOrUpdateJob(List<QDispatchVo> dispatchVm) throws SchedulerException {
        if (CheckUtil.isNullList(dispatchVm)) {
            return;
        }
        Map<JobDetail, Set<? extends Trigger>> triggersAndJobs = new HashMap<>(0);
        var scheduler = getScheduler();
        dispatchVm.forEach(st -> {
            //新增定时任务
            try {
                //获去调度器实例
                TriggerKey triggerKey = TriggerKey.triggerKey(st.getCode(), st.getDispatchGroup());
                CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
                Class clazz = Class.forName(st.getClassNames());
                clazz.getDeclaredConstructor().newInstance();
                // 构建job信息
                JobDetail jobDetail = JobBuilder.newJob(clazz).usingJobData(dealData(st)).withIdentity(st.getCode(), st.getDispatchGroup()).build();
                // 表达式调度构建器(即任务执行的时间)
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(st.getCron());
                TriggerBuilder triggerBuilder;
                if (trigger == null) {
                    // 按新的cronExpression表达式构建一个新的trigger
                      triggerBuilder = TriggerBuilder.newTrigger().withIdentity(st.getCode(), st.getDispatchGroup()).withSchedule(scheduleBuilder).endAt(st.getStopTime());
                } else {
                      triggerBuilder = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).endAt(st.getStopTime());
                }
                if (st.getOpen()){
                    trigger= (CronTrigger) triggerBuilder.startNow().build();
                }else {
                    trigger= (CronTrigger) triggerBuilder.build();
                }
                //设置job执行
                triggersAndJobs.put(jobDetail, Set.of(trigger));
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        });
        if (scheduler != null) {
            scheduler.scheduleJobs(triggersAndJobs, true);
            scheduler.start();
        }

    }
    /**
     * 暂停job
     */
    public void removeOrStopJob(QDispatchVo dispatchVm) {
        try {
            var scheduler = getScheduler();
            JobKey jobKey = JobKey.jobKey(dispatchVm.getCode(), dispatchVm.getDispatchGroup());
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 立即执行
     */
    public void runAJobNow(QDispatchVo dispatchVm) {
        try {
            var scheduler = getScheduler();
            JobKey jobKey = JobKey.jobKey(dispatchVm.getCode(), dispatchVm.getDispatchGroup());
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 获取所有的执行job
     */
    public List<PDispatchDispatchVm> getAllJob() {
        try {
            var scheduler = getScheduler();
            List<PDispatchDispatchVm> jobList = new ArrayList<PDispatchDispatchVm>();
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            jobKeys.forEach(jobKey -> {
                List<? extends Trigger> triggers = null;
                try {
                    triggers = scheduler.getTriggersOfJob(jobKey);
                } catch (SchedulerException e) {
                    throw new RuntimeException(e);
                }
                if (triggers != null) {
                    triggers.forEach(trigger -> {
                        PDispatchDispatchVm job = new PDispatchDispatchVm();
                        job.setName(jobKey.getName());
                        job.setDispatchGroup(jobKey.getGroup());
                        if (trigger instanceof CronTrigger) {
                            CronTrigger cronTrigger = (CronTrigger) trigger;
                            String cronExpression = cronTrigger.getCronExpression();
                            job.setCron(cronExpression);
                        }
                        jobList.add(job);
                    });
                }
            });
            return jobList;
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取正在运行的job
     */
    public List<PDispatchDispatchVm> getRunningJob() {
        try {
            var scheduler = getScheduler();
            List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
            var jobList = new ArrayList<PDispatchDispatchVm>();
            executingJobs.forEach(s -> {
                PDispatchDispatchVm job = new PDispatchDispatchVm();
                JobDetail jobDetail = s.getJobDetail();
                JobKey jobKey = jobDetail.getKey();
                job.setCode(jobKey.getName());
                job.setDispatchGroup(jobKey.getGroup());
                Trigger trigger = s.getTrigger();
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCron(cronExpression);
                }
                jobList.add(job);
            });
            return jobList;
        } catch (SchedulerException e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 暂停所有job
     */
    public void pauseAllJobs() {
        try {
            var scheduler = getScheduler();
            scheduler.pauseAll();
        } catch (SchedulerException e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 恢复所有job
     */
    public void resumeAllJobs() {
        try {
            var scheduler = getScheduler();
            scheduler.resumeAll();
        } catch (SchedulerException e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 设置参数
     *
     * @param dispatchVm
     * @return
     */
    private JobDataMap dealData(QDispatchVo dispatchVm) {
        JobDataMap jobDataMap = new JobDataMap();
        if (CheckUtil.isNotNullObject(dispatchVm.getParam())) {
            jobDataMap.putAll(JacksonUtil.parseObject(dispatchVm, Map.class));
        }
        log.info("调度参数" + JacksonUtil.toJSONStringPretty(jobDataMap));
        return jobDataMap;
    }

    private Scheduler getScheduler() {
        try {
            var scheduler = StdSchedulerFactory.getDefaultScheduler();
            return scheduler;
        } catch (SchedulerException e) {
            log.error(e.getMessage());
        }
        return null;
    }
}
